package com.gitee.faster.webdav.kernel.protocol;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.net.URLDecoder;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import com.gitee.faster.webdav.common.constant.Constants;
import com.gitee.faster.webdav.common.constant.MethodEnum;
import com.gitee.faster.webdav.common.helper.JsonHelper;
import com.gitee.faster.webdav.common.structure.StringValue;
import com.gitee.faster.webdav.common.helper.XmlHelper;
import com.gitee.faster.webdav.exception.ReadException;
import com.gitee.faster.webdav.exception.WriteException;
import com.gitee.faster.webdav.kernel.Configuration;
import com.gitee.faster.webdav.kernel.resource.Resource;
import com.gitee.faster.webdav.kernel.resource.ResourceRepository;
import com.gitee.faster.webdav.kernel.resource.ResourceRepositoryFactory;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.smartboot.http.common.enums.HttpStatus;
import org.smartboot.http.server.HttpRequest;
import org.smartboot.http.server.HttpResponse;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
 * 提供请求与响应的读写方法
 *
 * @author hongda.li
 */
@Slf4j
public final class HttpExchange {

    /**
     * 请求
     */
    private final HttpRequest request;

    /**
     * 响应
     */
    private final HttpResponse response;

    /**
     * 查询参数
     */
    @Getter
    private final StringValue queryValue;

    /**
     * 请求头参数
     */
    @Getter
    private final StringValue headerValue;

    /**
     * 全局配置
     */
    @Getter
    private final Configuration configuration = Configuration.getInstance();

    /**
     * 资源库
     */
    @Getter
    private final ResourceRepository<Resource> repository;

    /**
     * 请求路径 (已解码)
     */
    @Getter
    private final String path;

    /**
     * 请求类型
     */
    @Getter
    private final String contentType;

    /**
     * 请求方法
     */
    @Getter
    private final MethodEnum methodEnum;

    /**
     * 当前请求资源
     */
    private Resource resource;

    public HttpExchange(HttpRequest request, HttpResponse response) {
        this.request = request;
        this.response = response;
        this.contentType = this.request.getContentType();
        this.headerValue = new StringValue(this.request::getHeader);
        this.queryValue = new StringValue(this.request::getParameter);
        this.methodEnum = MethodEnum.byName(this.request.getMethod());
        this.path = URLDecoder.decode(this.request.getRequestURI(), StandardCharsets.UTF_8);
        this.repository = ResourceRepositoryFactory.getInstance().findRepository(this.path);
    }

    /**
     * 从输入流中读取 Body 参数
     *
     * @return 读取结果
     */
    public String readFromInputStream() {
        return StrUtil.str(IoUtil.readBytes(this.getInputStream(), false), StandardCharsets.UTF_8);
    }

    public String getRequestMethod() {
        return this.request.getMethod();
    }

    /**
     * 将 Body 参数解析为 XML 格式的数据
     *
     * @param clazz 待转换的类型
     * @param <T>   泛型
     * @return JavaBean - XML
     */
    public <T> T parseXmlData(Class<T> clazz) {
        return XmlHelper.fromXml(this.readFromInputStream(), clazz);
    }

    /**
     * 设置状态码
     *
     * @param httpStatus 状态码
     */
    public void setHttpStatus(HttpStatus httpStatus) {
        this.response.setHttpStatus(httpStatus);
    }


    /**
     * 设置 Content-Type
     *
     * @param contentType Content-Type 内容
     */
    public void setContentType(String contentType) {
        this.response.setContentType(contentType);
    }

    /**
     * 设置 Content-Type; charset=UTF-8
     *
     * @param contentType Content-Type 内容
     */
    public void setContentTypeWithUtf8(String contentType) {
        this.response.setContentType(contentType + Constants.CONTENT_TYPE_SUFFIX + StandardCharsets.UTF_8.name());
    }

    /**
     * 设置响应数据大小
     *
     * @param contentLength 响应数据大小
     */
    public void setContentLength(long contentLength) {
        this.response.setContentLength(contentLength);
    }

    /**
     * 返回 XML 数据
     *
     * @param data XML 数据
     */
    public void writeXmlResult(Object data) {
        this.setContentTypeWithUtf8(Constants.XML_CONTENT_TYPE);
        try {
            String xml = XmlHelper.toXml(data);
            if (log.isDebugEnabled()) {
                log.debug("响应内容 : {}\n{}", Constants.LINE_START_SYMBOL, XmlUtil.format(xml));
            }
            this.response.write(xml.getBytes(StandardCharsets.UTF_8));
        } catch (Exception e) {
            throw new WriteException("写入响应数据异常", e);
        }
    }

    /**
     * 返回 HTML 数据
     *
     * @param data HTML 数据
     */
    public void writeHtmlResult(String data) {
        this.setContentTypeWithUtf8(Constants.HTML_CONTENT_TYPE);
        try {
            this.response.write(data.trim().getBytes(StandardCharsets.UTF_8));
        } catch (Exception e) {
            throw new WriteException("写入响应数据异常", e);
        }
    }

    public void writeJsonResult(Object data) {
        this.setContentTypeWithUtf8(Constants.JSON_CONTENT_TYPE);
        try {
            this.response.write(JsonHelper.toJson(data).getBytes(StandardCharsets.UTF_8));
        } catch (Exception e) {
            throw new WriteException("写入响应数据异常", e);
        }
    }

    /**
     * 添加请求头
     *
     * @param name  请求头名称
     * @param value 请求头的值
     */
    public void addHeader(String name, String value) {
        this.response.addHeader(name, value);
    }

    /**
     * 获取请求头名称列表
     *
     * @return 请求头
     */
    public List<String> getHeaderNames() {
        return new ArrayList<>(this.request.getHeaderNames());
    }

    /**
     * 获取输出流
     *
     * @return 输出流
     */
    public OutputStream getOutputStream() {
        return this.response.getOutputStream();
    }

    /**
     * 获取输入流
     *
     * @return 输入流
     */
    public InputStream getInputStream() {
        try {
            return this.request.getInputStream();
        } catch (IOException e) {
            throw new ReadException("获取请求数据出现异常", e);
        }
    }

    /**
     * 获取当前请求对应的资源
     * 若资源尚未加载则加载当前资源
     *
     * @return 请求资源
     */
    public Resource getResource() {
        if (this.resource == null) {
            synchronized (HttpExchange.class) {
                if (this.resource != null) {
                    return this.resource;
                } else {
                    this.resource = this.repository.transform(this.path);
                }
            }
        }
        return resource;
    }
}
